/*  
 * Copyright 2007 MBARI 
 * 
 * Unless required by applicable law or agreed to in writing, software 
 * distributed under the License is distributed on an "AS IS" BASIS, 
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
 * See the License for the specific language governing permissions and 
 * limitations under the License. 
 */
package org.mbari.timeline.piccolo;

import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.awt.geom.Rectangle2D;

import org.mbari.timeline.DateUtil;
import org.mbari.timeline.model.MultipartEvent;

import com.domainlanguage.time.Duration;
import com.domainlanguage.time.TimeInterval;
import com.domainlanguage.time.TimePoint;

import edu.umd.cs.piccolo.PCanvas;
import edu.umd.cs.piccolo.activities.PTransformActivity;
import edu.umd.cs.piccolo.event.PDragEventHandler;
import edu.umd.cs.piccolo.event.PInputEvent;
import edu.umd.cs.piccolo.util.PBounds;
import edu.umd.cs.piccolo.util.PDimension;
import edu.umd.cs.piccolo.util.PPaintContext;
import edu.umd.cs.piccolox.PFrame;

/**
 * A very simple class, this is the canvas that the main Timeline component gets added
 * to. The only significant logic executing here is in the addScrollListeners method,
 * which handles the mouse drag listener for moving the calendar and the keyboard
 * listener for left and right key events.
 * 
 * @author Andrew Chase
 *
 */
@SuppressWarnings("serial")
public class TimelineCanvas extends PCanvas {
	/**
	 * When the user presses an arrow key the timeline will be shifted left or
	 * right. This offset value defines the amount of overlap between the
	 * non-shifted and shifted view of the timeline.
	 */
	private static final double JUMP_OFFSET = 250;

	/**
	 * The amount of time, in milliseconds, to allow for a jump transition (per
	 * pixel jumped)
	 */
	private static final double JUMP_TIME_PER_PIXEL = .75;

	private Timeline timeline;

	public TimelineCanvas(Timeline timeline) {
		super();
		this.timeline = timeline;
		initialize();
	}

	void initialize() {
		this.setDefaultRenderQuality(PPaintContext.LOW_QUALITY_RENDERING);
		this.setBackground(Color.black);
		addScrollListeners();
		// push the timeline down into the middle of the screen a bit.
		timeline.translate(40, 50);
		getLayer().addChild(timeline);
		setBounds(20, 20, 800, 200);
	}

	private void addScrollListeners() {
		removeInputEventListener(getPanEventHandler());
		// only allow dragging horizontally
		addInputEventListener(new PDragEventHandler() {
			public void drag(PInputEvent event) {
				PDimension canvasDelta = event.getCanvasDelta();
				getCamera().translateView(canvasDelta.width, 0);
				event.setHandled(true);
			}
		});

		// This is the key listener for jumping left or right when the arrow
		// keys are used.
		addKeyListener(new KeyAdapter() {
			private PTransformActivity lastActivity;

			public void keyPressed(KeyEvent event) {
				if (!isDirectionKey(event)) {
					return;
				}
				int direction = getDirectionOfOffset(event);
				if (lastActivity != null && lastActivity.isStepping()) {
					return;
				}
				shiftBounds(direction);
			}

			private void shiftBounds(int direction) {
				Rectangle2D shiftedBounds = getShiftedCameraBounds(direction,
						JUMP_OFFSET);
				long timeToJump = getJumpTime(shiftedBounds);
				lastActivity = getCamera().animateViewToPanToBounds(
						shiftedBounds, timeToJump);
			}

			private long getJumpTime(Rectangle2D shiftedBounds) {
				double delta = Math.abs(shiftedBounds.getMinX()
						- getCamera().getViewBounds().getMinX());
				return (long) (JUMP_TIME_PER_PIXEL * delta);
			}

			private int getDirectionOfOffset(KeyEvent event) {
				int keyCode = event.getKeyCode();
				int direction = 0;
				if (keyCode == KeyEvent.VK_RIGHT) {
					direction = 1;
				} else if (keyCode == KeyEvent.VK_LEFT) {
					direction = -1;
				}
				return direction;
			}

			private boolean isDirectionKey(KeyEvent event) {
				int direction = getDirectionOfOffset(event);
				return direction != 0;
			}

			private Rectangle2D getShiftedCameraBounds(int direction,
					double offset) {
				PBounds cameraBounds = getCamera().getViewBounds();
				double newX = cameraBounds.x
						+ (cameraBounds.width * direction - offset * direction);
				return new Rectangle2D.Double(newX, cameraBounds.y,
						cameraBounds.width, cameraBounds.height);
			}
		});
	}

	public static void main(String[] arg) {

		TimePoint start = DateUtil
				.ansiStandardDateStringToDate("20070707-00:00:00");
		TimePoint end = DateUtil
				.ansiStandardDateStringToDate("20070807-00:00:00");
		TimeInterval range = new TimeInterval(start, true, end, true);
		final Timeline timeline = new Timeline(range);

		MultipartEvent event1 = MultipartEvent.twoPartEvent( //
				start.plus(Duration.days(2).plus(Duration.hours(3))), //
				Duration.minutes(35), Duration.minutes(180));
		final TwoPartEventNode eventNode = new TwoPartEventNode(event1,
				timeline);

		MultipartEvent event2 = MultipartEvent.twoPartEvent(start.plus(Duration
				.days(5)), // 
				Duration.minutes(60), Duration.days(1));
		final TwoPartEventNode eventNode2 = new TwoPartEventNode(event2,
				timeline);
		eventNode2.getRightEdge().setStrokePaint(Color.magenta);
		BasicStroke testStroke = new BasicStroke(4);
		eventNode2.getRightEdge().setStroke(testStroke);
		timeline.addEventNode(eventNode);
		timeline.addEventNode(eventNode2);

		TimelineCanvas canvas = new TimelineCanvas(timeline);
		PFrame frame = new PFrame("Test Timeline", false, canvas);
		frame.setBounds(100, 100, 1300, 400);
		frame.setVisible(true);
	}
}
